import stockData from './stockMeta.js';
import { initializeTimeSeriesData, TIME_SERIES_LENGTH } from './timeSeriesData.js';
import { createMouseInteractionController } from './mouseInteraction.js';

/**
 * 创建颜色配置对象，用于根据涨跌幅计算立方体颜色
 * @param {Object} options 配置选项
 * @param {number} options.maxAbsIncrease 最大涨跌幅绝对值，用于归一化
 * @returns {Object} 颜色配置对象，包含getColor方法
 */
function createColorConfig(options = {}) {
    const {
        maxAbsIncrease = 0.05
    } = options;

    return {
        increaseColors: [
            new THREE.Color(0xD0D0D0),
            new THREE.Color(0xFFCCCC),
            new THREE.Color(0xFF9999),
            new THREE.Color(0xFF6666),
            new THREE.Color(0xFF3333),
            new THREE.Color(0xCC0000)
        ],
        decreaseColors: [
            new THREE.Color(0xD0D0D0),
            new THREE.Color(0xCCFFCC),
            new THREE.Color(0x99FF99),
            new THREE.Color(0x66FF66),
            new THREE.Color(0x33FF33),
            new THREE.Color(0x00CC00)
        ],
        maxAbsIncrease,
        /**
         * 根据涨跌幅计算颜色
         * @param {number} increase 涨跌幅值
         * @returns {THREE.Color} 计算后的颜色对象
         */
        getColor: function (increase) {
            if (Math.abs(increase) < 0.001) {
                return new THREE.Color(0xD0D0D0);
            }

            let colors, progress;
            if (increase > 0) {
                // 上涨：使用红色系渐变，涨幅越大颜色越深红
                colors = this.increaseColors;
                progress = Math.min(increase / this.maxAbsIncrease, 1);
            } else {
                // 下跌：使用绿色系渐变，跌幅越大颜色越深绿
                colors = this.decreaseColors;
                progress = Math.min(Math.abs(increase) / this.maxAbsIncrease, 1);
            }

            // 计算颜色索引和混合比例
            // 直接使用进度映射到颜色数组，确保涨跌幅越大使用更深的颜色
            const maxIndex = colors.length - 1;
            const colorIndex = Math.max(
                Math.floor(progress * maxIndex),
                1 // 确保至少使用第二个颜色，避免完全灰色
            );

            const blendFactor = (progress * maxIndex) - Math.floor(progress * maxIndex);

            // 线性插值计算最终颜色
            const finalColor = new THREE.Color();
            finalColor.lerpColors(
                colors[colorIndex],
                colors[Math.min(colorIndex + 1, maxIndex)], // 向更深色方向插值
                blendFactor
            );

            return finalColor;
        }
    };
}

// 配置常量
const GRID_COLUMNS = 20; // 网格列数
const GRID_ROWS = 20;    // 网格行数
const SIZE_SCALE_FACTOR = 2; // 大小缩放因子
const FIXED_CUBE_SIZE = 25 * SIZE_SCALE_FACTOR; // 固定立方体大小

// 时间动态相关配置
const ANIMATION_SPEED = 300; // 动画速度（毫秒）
let timeSeriesData = []; // 存储时间序列数据
let currentTimeIndex = 0; // 当前时间索引
let increaseData = {}; // 当前涨跌幅数据
let isPlaying = false; // 动画播放状态
let animationInterval = null; // 动画间隔ID
let animationSpeed = ANIMATION_SPEED; // 当前动画速度
let showTrails = true; // 是否显示轨迹

// 当前排序方式，默认为按weight排序
let currentSortMethod = 'weight'; // 'weight' 或 'increase'
timeSeriesData = initializeTimeSeriesData();
increaseData = { ...timeSeriesData[0] };


// 初始化场景
const scene = new THREE.Scene();
scene.background = new THREE.Color(0xE0E0E0); // 偏灰色背景色

// 初始化相机，设置合理的远裁剪面以适应所有缩放级别
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 10000);
// 调整相机位置
camera.position.set(12, 20, 12); // 提高相机高度和距离，扩大视野
camera.lookAt(0, 0, 0); // 相机看向原点

// 初始化渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// 从JSON数据生成山峰
function generateMountainsFromJSON(data) {
    // 使用公共的颜色配置方法
    const colorConfig = createColorConfig();

    // 存储所有山峰及其信息
    const mountainData = [];

    // 计算所有weight的总和
    const totalWeight = data.reduce((sum, item) => sum + item.weight, 0);

    // 为按照大小紧密排列准备数据
    data.forEach((item, index) => {
        // 调整weight，使总和为100
        const adjustedWeight = (item.weight / totalWeight) * 100;
        // 使用调整后的weight作为体积
        const volume = adjustedWeight;
        mountainData.push({
            volume: volume, // 保存调整后的体积值
            weight: item.weight,
            name: item.name,
            code: item.code
        });
    });

    // 根据当前选择的排序方式进行排序
    if (currentSortMethod === 'weight') {
        mountainData.sort((a, b) => {
            return a.weight - b.weight;
        });
    } else {
        // 按涨跌幅排序
        mountainData.sort((a, b) => {
            const incA = increaseData[a.code] || 0;
            const incB = increaseData[b.code] || 0;
            return incA - incB;
        });
    }

    // 计算体积范围用于颜色映射
    const volumes = mountainData.map(m => m.volume);
    const minVolume = Math.min(...volumes);
    const maxVolume = Math.max(...volumes);
    const volumeRange = maxVolume - minVolume || 1;

    // 为每个山峰创建几何体和材质，并设置颜色
    mountainData.forEach((item, index) => {
        // 存储原始数据，用于时间序列更新
        item.originalData = {
            volume: item.volume,
            weight: item.weight,
            name: item.name,
            code: item.code
        };

        // 获取当前涨跌幅数据
        const currentIncrease = increaseData[item.code] || 0;

        // 使用科学缩放方法，基于weight属性和涨跌幅计算立方体高度
        const rawWeight = item.weight;

        // 计算所有weight的最大值和最小值，用于归一化
        const allWeights = mountainData.map(m => m.weight);
        const minWeight = Math.min(...allWeights);
        const maxWeight = Math.max(...allWeights);
        const weightRange = maxWeight - minWeight;

        // 科学缩放 - 使用对数缩放避免高度差异过大或过小
        const normalizedWeight = weightRange > 0 ? (rawWeight - minWeight) / weightRange : 0.5;
        const scaledWeight = Math.log1p(normalizedWeight * 10);

        // 设置基础高度参数
        const baseHeight = 150; // 基础高度
        const weightScaleFactor = 25; // 权重缩放因子
        const increaseScaleFactor = 500; // 涨跌幅缩放因子（使涨跌幅变化更明显）
        const minHeight = 50; // 最小高度，确保最小的立方体也能看清

        // 计算最终高度：基础高度 + 权重高度 + 涨跌幅高度
        const weightHeight = baseHeight + scaledWeight * weightScaleFactor;
        const calculatedHeight = weightHeight + currentIncrease * increaseScaleFactor;
        const height = Math.max(minHeight, calculatedHeight);

        // 使用固定大小的立方体
        const width = FIXED_CUBE_SIZE * 0.9;
        const depth = FIXED_CUBE_SIZE * 0.9;

        // 使用BoxBufferGeometry创建立方体
        const geometry = new THREE.BoxBufferGeometry(width, height, depth);

        // 根据涨跌幅设置颜色
        let cubeColor = colorConfig.getColor(currentIncrease);

        // 创建材质，增加边框效果提高立方体之间的区分度
        const material = new THREE.MeshPhongMaterial({
            color: cubeColor,
            shininess: 60, // 增加光泽度
            emissive: cubeColor.multiplyScalar(0.2), // 添加适当的自发光效果
            wireframe: false,
            side: THREE.FrontSide,
            transparent: false
        });

        const mountain = new THREE.Mesh(geometry, material);
        // 存储必要的属性，用于后续更新
        mountain.code = item.code;
        mountain.originalWeight = item.weight;
        mountain.name = `${item.name} (${item.code}) - 涨跌幅: ${(currentIncrease * 100).toFixed(2)}%`;

        // 设置立方体位置，使其底部与地面接触
        mountain.position.y = height / 2;

        // 更新山峰数据
        item.mountain = mountain;
    });

    // 根据当前选择的排序方式进行排序
    if (currentSortMethod === 'weight') {
        // 按weight属性排序，weight大的在前
        mountainData.sort((a, b) => {
            //return b.weight - a.weight;
            return a.weight - b.weight;
        });
    } else {
        // 按涨跌幅排序，涨幅大的在前
        mountainData.sort((a, b) => {
            const incA = increaseData[a.code] || 0;
            const incB = increaseData[b.code] || 0;
            //return incB - incA;
            return incA - incB;
        });
    }

    // 设置每个立方体的位置并添加到场景
    const mountains = [];

    // 显示所有立方体
    const displayCount = mountainData.length;

    // 设置固定大小的立方体，以便排满画布
    const sizeScaleFactor = 2; // 长宽放大2倍
    let fixedCubeSize = 40 * sizeScaleFactor; // 固定立方体大小，乘以放大倍数（使用let以便后续修改）


    // 计算总宽度和总深度
    const totalWidth = GRID_COLUMNS * FIXED_CUBE_SIZE;
    const totalDepth = GRID_ROWS * FIXED_CUBE_SIZE;

    // 为每个立方体设置固定大小和属性
    mountainData.forEach((item, index) => {
        const increase = increaseData[item.code] || 0;
        item.mountain.name = `${item.name} (${item.code}) - 涨跌幅: ${(increase * 100).toFixed(2)}%`; // 设置名称，包含代码和涨跌幅

        // 计算高度时考虑涨跌幅
        const baseHeight = 150; // 基础高度
        const weightScaleFactor = 25; // 权重缩放因子
        const increaseScaleFactor = 500; // 涨跌幅缩放因子
        const minHeight = 50; // 最小高度

        // 计算权重高度
        const allWeights = mountainData.map(m => m.weight);
        const minWeight = Math.min(...allWeights);
        const maxWeight = Math.max(...allWeights);
        const weightRange = maxWeight - minWeight;
        const normalizedWeight = weightRange > 0 ? (item.weight - minWeight) / weightRange : 0.5;
        const scaledWeight = Math.log1p(normalizedWeight * 10);

        // 计算最终高度：基础高度 + 权重高度 + 涨跌幅高度
        const weightHeight = baseHeight + scaledWeight * weightScaleFactor;
        const calculatedHeight = weightHeight + increase * increaseScaleFactor;
        const height = Math.max(minHeight, calculatedHeight);

        // 重新创建立方体几何体以使用固定大小
        const newGeometry = new THREE.BoxBufferGeometry(FIXED_CUBE_SIZE * 0.9, height, FIXED_CUBE_SIZE * 0.9); // 略微减小立方体尺寸，增加间距效果
        item.mountain.geometry.dispose(); // 释放旧几何体
        item.mountain.geometry = newGeometry; // 设置新几何体
        item.mountain.position.y = height / 2; // 重置Y位置

        // 为立方体添加边框，增强区分度
        const edges = new THREE.EdgesGeometry(item.mountain.geometry);
        const edgeMaterial = new THREE.LineBasicMaterial({
            color: 0x333333, // 深色边框
            linewidth: 1
        });
        const edgeMesh = new THREE.LineSegments(edges, edgeMaterial);
        item.mountain.add(edgeMesh); // 将边框添加到立方体上

        // 在立方体顶面添加名称和涨跌幅文本
        const itemIncrease = increaseData[item.code] || 0;
        addTextToCube(item.mountain, item.name, itemIncrease, height, item.mountain.material.color, fixedCubeSize);

        // 为立方体的四个侧面添加文字
        const sides = ['front', 'back', 'left', 'right'];
        sides.forEach(side => {
            addSideTextToCube(item.mountain, item.name, item.code, height, item.mountain.material.color, side);
        });
    });

    // 计算起始位置，使整个矩形居中，确保立方体不会超出网格范围
    const startX = -totalWidth / 2;
    const startZ = -totalDepth / 2;

    // 按照大小顺序在网格内排列所有立方体，类似换行显示
    for (let i = 0; i < displayCount; i++) {
        const item = mountainData[i];

        // 计算网格坐标（类似表格布局，从左到右，从上到下）
        const col = i % GRID_COLUMNS;
        const row = Math.floor(i / GRID_COLUMNS);

        // 计算实际位置，确保立方体紧密排列、充满画布且不超出网格范围
        const positionX = startX + col * FIXED_CUBE_SIZE + FIXED_CUBE_SIZE / 2;
        const positionZ = startZ + row * FIXED_CUBE_SIZE + FIXED_CUBE_SIZE / 2;

        // 设置立方体位置，保持正确的y轴位置
        item.mountain.position.set(positionX, item.mountain.position.y, positionZ);

        scene.add(item.mountain);
        mountains.push(item.mountain);
    }

    return mountains;
}

// 创建射线投射器，用于检测鼠标是否悬停在山峰上
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();

// 创建显示名字的div元素
const tooltip = document.createElement('div');
tooltip.className = 'tooltip';
document.body.appendChild(tooltip);

// 最近悬停的山峰对象
let hoveredMountain = null;

// 更新鼠标位置
function updateMousePosition(event) {
    mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
    mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
}

// 检测鼠标悬停
function detectHoveredMountain(event) {
    updateMousePosition(event);

    // 更新射线投射器
    raycaster.setFromCamera(mouse, camera);

    // 计算与所有山峰的交点
    const intersects = raycaster.intersectObjects(mountains || []);

    // 如果有交点
    if (intersects.length > 0) {
        const intersect = intersects[0];
        const mountain = intersect.object;

        // 如果悬停在新的山峰上
        if (mountain !== hoveredMountain) {
            hoveredMountain = mountain;

            // 动态获取当前时间点的涨跌幅数据，而不是使用静态的mountain.name
            const currentIncrease = increaseData[mountain.code] || 0;
            const increasePercent = (currentIncrease * 100).toFixed(2) + '%';

            // 提取mountain.name中的名称部分（不含涨跌幅信息）
            let displayName = mountain.name;
            if (displayName.includes(' - 涨跌幅:')) {
                displayName = displayName.split(' - 涨跌幅:')[0];
            }

            // 设置tooltip文本为名称和当前时间点的涨跌幅
            tooltip.textContent = `${displayName} - 涨跌幅: ${increasePercent}`;
            tooltip.style.left = event.clientX + 10 + 'px';
            tooltip.style.top = event.clientY - 30 + 'px';
            tooltip.style.display = 'block';
        } else {
            // 更新tooltip位置和内容，确保始终显示最新数据
            const currentIncrease = increaseData[mountain.code] || 0;
            const increasePercent = (currentIncrease * 100).toFixed(2) + '%';

            let displayName = mountain.name;
            if (displayName.includes(' - 涨跌幅:')) {
                displayName = displayName.split(' - 涨跌幅:')[0];
            }

            tooltip.textContent = `${displayName} - 涨跌幅: ${increasePercent}`;
            tooltip.style.left = event.clientX + 10 + 'px';
            tooltip.style.top = event.clientY - 30 + 'px';
        }
    } else {
        // 没有悬停在任何山峰上
        if (hoveredMountain !== null) {
            hoveredMountain = null;
            tooltip.style.display = 'none';
        }
    }
}

// 添加鼠标移动事件监听器
document.addEventListener('mousemove', detectHoveredMountain);

// 初始相机位置
camera.position.set(0, 300, 300); // 默认适中的相机位置
camera.lookAt(0, 0, 0);

// 添加更强的光源，提高整体对比度
const ambientLight = new THREE.AmbientLight(0x808080); // 增强环境光
scene.add(ambientLight);

const directionalLight = new THREE.DirectionalLight(0xffffff, 1.2); // 增强主光源
directionalLight.position.set(2, 3, 2); // 调整光源位置
scene.add(directionalLight);

// 添加辅助光源，提高立体感
const fillLight = new THREE.DirectionalLight(0xffffff, 0.6);
fillLight.position.set(-3, 2, -1);
scene.add(fillLight);

// 添加坐标轴辅助，使用默认颜色但增加长度以提高可见性
const axesHelper = new THREE.AxesHelper(600); // 增加长度提高可见性
scene.add(axesHelper);

// 添加网格辅助，调整颜色提高对比度
// 先创建默认的网格辅助，避免undefined错误
let gridHelper = new THREE.GridHelper(1000, 20, 0x333355, 0x222244); // 深蓝色网格，与背景协调
scene.add(gridHelper);

// 声明全局mountains变量
let mountains = [];

// 更新立方体的高度和颜色以反映当前时间点的数据
function updateCubeForTimePoint(cube, newIncrease) {
    const baseHeight = 150; // 基础高度
    const weightScaleFactor = 25; // 权重缩放因子
    const increaseScaleFactor = 500; // 涨跌幅缩放因子
    const minHeight = 50; // 最小高度

    // 获取立方体的原始权重数据
    const rawWeight = cube.originalWeight;

    // 计算所有立方体的权重范围（简化版本，实际应用中可能需要缓存这个值）
    const allWeights = mountains.map(m => m.originalWeight);
    const minWeight = Math.min(...allWeights);
    const maxWeight = Math.max(...allWeights);
    const weightRange = maxWeight - minWeight;

    // 计算归一化权重和缩放权重
    const normalizedWeight = weightRange > 0 ? (rawWeight - minWeight) / weightRange : 0.5;
    const scaledWeight = Math.log1p(normalizedWeight * 10);

    // 计算新高度：基础高度 + 权重高度 + 涨跌幅高度
    const weightHeight = baseHeight + scaledWeight * weightScaleFactor;
    const newHeight = Math.max(minHeight, weightHeight + newIncrease * increaseScaleFactor);

    // 使用公共的颜色配置方法
    const colorConfig = createColorConfig();

    // 计算颜色
    const calculatedColor = colorConfig.getColor(newIncrease);
    console.log(`Cube: ${cube.name}, Increase: ${newIncrease.toFixed(4)}, Color: ${calculatedColor.getHexString()}`);

    // 应用颜色 - 确保不使用默认白色
    cube.material.color.copy(calculatedColor);
    cube.material.emissive.copy(calculatedColor).multiplyScalar(0.2);
    // 确保材质类型正确
    if (cube.material.type !== 'MeshPhongMaterial') {
        console.warn(`Cube ${cube.name} has non-standard material: ${cube.material.type}`);
    }

    // 更新几何体和位置
    const newGeometry = new THREE.BoxBufferGeometry(FIXED_CUBE_SIZE * 0.9, newHeight, FIXED_CUBE_SIZE * 0.9);
    cube.geometry.dispose();
    cube.geometry = newGeometry;
    cube.position.y = newHeight / 2;

    // 更新文本
    updateCubeText(cube, newIncrease, newHeight);
}

// 更新立方体上的文本
function updateCubeText(cube, newIncrease, newHeight) {
    // 移除旧的文本对象
    cube.children = cube.children.filter(child =>
        !(child.geometry instanceof THREE.PlaneGeometry)
    );

    // 添加新的顶面文本
    const cubeName = cube.name.split(' - ')[0]; // 提取名称部分
    addTextToCube(cube, cubeName, newIncrease, newHeight, cube.material.color, FIXED_CUBE_SIZE);

    // 添加侧面文本
    const name = cubeName.split(' (')[0];
    const code = cubeName.split(' (')[1]?.replace(')', '') || '';
    const sides = ['front', 'back', 'left', 'right'];
    sides.forEach(side => {
        addSideTextToCube(cube, name, code, newHeight, cube.material.color, side);
    });
}

// 创建高度轨迹效果
function createHeightTrail(cube, previousHeight) {
    if (!showTrails || previousHeight <= 0) return;

    // 创建半透明的轨迹几何体
    const trailGeometry = new THREE.BoxBufferGeometry(
        FIXED_CUBE_SIZE * 0.9,
        previousHeight,
        FIXED_CUBE_SIZE * 0.9
    );

    // 创建半透明材质
    const trailMaterial = new THREE.MeshBasicMaterial({
        color: cube.material.color,
        transparent: true,
        opacity: 0.2
    });

    // 创建轨迹网格
    const trail = new THREE.Mesh(trailGeometry, trailMaterial);
    trail.position.set(cube.position.x, previousHeight / 2, cube.position.z);

    // 添加到场景
    scene.add(trail);

    // 设置定时器移除轨迹
    setTimeout(() => {
        scene.remove(trail);
        trailGeometry.dispose();
        trailMaterial.dispose();
    }, 1000); // 1秒后移除轨迹
}

// 切换到指定时间点
function switchToTimePoint(timeIndex) {
    if (timeIndex < 0 || timeIndex >= timeSeriesData.length) return;

    currentTimeIndex = timeIndex;
    const newData = timeSeriesData[timeIndex];

    // 更新所有立方体
    mountains.forEach(cube => {
        const code = cube.code;
        const newIncrease = newData[code] || 0;
        const previousHeight = cube.geometry.parameters.height; // 保存之前的高度用于轨迹

        // 创建轨迹
        // createHeightTrail(cube, previousHeight);

        // 更新立方体
        updateCubeForTimePoint(cube, newIncrease);

        // 更新当前涨跌幅数据
        increaseData[code] = newIncrease;
    });

    // 更新时间显示
    updateTimeDisplay();
}

// 更新时间显示
function updateTimeDisplay() {
    const timeDisplay = document.getElementById('time-display');
    if (timeDisplay) {
        timeDisplay.textContent = `时间点: ${currentTimeIndex + 1} / ${TIME_SERIES_LENGTH}`;
    }

    // 更新进度条
    const progressBar = document.getElementById('time-progress');
    if (progressBar) {
        progressBar.value = currentTimeIndex;
    }
}

// 播放/暂停动画
function togglePlayback() {
    isPlaying = !isPlaying;

    const playButton = document.getElementById('play-button');
    if (playButton) {
        playButton.textContent = isPlaying ? '暂停' : '播放';
    }

    if (isPlaying) {
        startPlayback();
    } else {
        stopPlayback();
    }
}

// 开始播放动画
function startPlayback() {
    if (animationInterval) return;

    animationInterval = setInterval(() => {
        currentTimeIndex = (currentTimeIndex + 1) % TIME_SERIES_LENGTH;
        switchToTimePoint(currentTimeIndex);
    }, animationSpeed);
}

// 停止播放动画
function stopPlayback() {
    if (animationInterval) {
        clearInterval(animationInterval);
        animationInterval = null;
    }
}

// 上一个时间点
function previousTimePoint() {
    stopPlayback();
    isPlaying = false;
    const playButton = document.getElementById('play-button');
    if (playButton) {
        playButton.textContent = '播放';
    }

    currentTimeIndex = (currentTimeIndex - 1 + TIME_SERIES_LENGTH) % TIME_SERIES_LENGTH;
    switchToTimePoint(currentTimeIndex);
}

// 下一个时间点
function nextTimePoint() {
    stopPlayback();
    isPlaying = false;
    const playButton = document.getElementById('play-button');
    if (playButton) {
        playButton.textContent = '播放';
    }

    currentTimeIndex = (currentTimeIndex + 1) % TIME_SERIES_LENGTH;
    switchToTimePoint(currentTimeIndex);
}

// 更新动画速度
function updateAnimationSpeed(speed) {
    animationSpeed = speed;
    if (isPlaying) {
        stopPlayback();
        startPlayback();
    }
}

// 切换轨迹显示
function toggleTrails() {
    showTrails = !showTrails;
    const trailsCheckbox = document.getElementById('show-trails');
    if (trailsCheckbox) {
        trailsCheckbox.checked = showTrails;
    }
}

// 为立方体添加顶面文字
function addTextToCube(cube, name, increase, cubeHeight) {
 
    // 创建Canvas作为文字纹理
    const canvas = document.createElement('canvas');
    const context = canvas.getContext('2d');

    // 设置字体大小
    const fontSize = 14;
    const increaseFontSize = 12;
    context.font = `${fontSize}px Arial`;

    // 限制名称长度
    const maxNameLength = 10;
    let displayName = name;
    if (name.length > maxNameLength) {
        displayName = name.substring(0, maxNameLength) + '...';
    }

    // 格式化涨跌幅为百分比，保留两位小数
    const increasePercent = (increase * 100).toFixed(2) + '%';

    // 测量文本宽度和高度
    const nameWidth = context.measureText(displayName).width + 10;
    context.font = `${increaseFontSize}px Arial`;
    const increaseWidth = context.measureText(increasePercent).width + 10;

    const textWidth = Math.max(nameWidth, increaseWidth);
    const textHeight = (fontSize + increaseFontSize + 12); // 两行文字高度加上间距

    // 设置canvas尺寸
    canvas.width = textWidth;
    canvas.height = textHeight;

    // 重新设置字体
    context.font = `${fontSize}px Arial`;

    // 设置文本对齐方式
    context.textAlign = 'center';
    context.textBaseline = 'middle';

    // 强制使用白色文字以确保最高可见性
    context.fillStyle = 'rgba(255, 255, 255, 1)';

    // 添加黑色描边以提高对比度和可读性
    context.strokeStyle = 'rgba(0, 0, 0, 1)';
    context.lineWidth = 3; // 增加描边宽度以提高文字清晰度

    // 添加阴影效果以增强文字的立体感
    context.shadowColor = 'rgba(0, 0, 0, 0.8)';
    context.shadowBlur = 4;
    context.shadowOffsetX = 1;
    context.shadowOffsetY = 1;

    // 绘制第一行：名称
    context.fillText(displayName, textWidth / 2, fontSize + 2);

    // 绘制第二行：涨跌幅
    context.font = `${increaseFontSize}px Arial`;
    context.fillText(increasePercent, textWidth / 2, fontSize + increaseFontSize + 8);

    // 创建纹理
    const texture = new THREE.CanvasTexture(canvas);
    texture.needsUpdate = true;

    // 创建文字材质
    const textMaterial = new THREE.MeshBasicMaterial({
        map: texture,
        transparent: true,
        side: THREE.DoubleSide
    });

    // 创建立方体顶面大小的平面几何体
    const textGeometry = new THREE.PlaneGeometry(FIXED_CUBE_SIZE * 0.8, FIXED_CUBE_SIZE * 0.4); // 文字板的大小

    // 创建文字网格
    const textMesh = new THREE.Mesh(textGeometry, textMaterial);

    // 设置文字位置在立方体顶面中心
    textMesh.position.set(0, cubeHeight / 2 + 0.1, 0);

    // 使用负角度旋转，确保从顶部观看时文字不被镜像
    textMesh.rotation.x = -Math.PI / 2;

    // 将文字添加到立方体
    cube.add(textMesh);

    // 确保文字可见性设置正确
    textMesh.visible = true;
    textMesh.material.visible = true;
    textMesh.material.opacity = 1.0;
}

// 为立方体侧面添加文字（name和code都竖向显示，且之间换行）
function addSideTextToCube(cube, name, code, cubeHeight, cubeColor, side) {
    // 创建文字材质 - 使用相反色调以提高可读性
    const textColor = cubeColor.r > 0.5 ? 0x000000 : 0xFFFFFF;

    // 创建Canvas作为文字纹理
    const canvas = document.createElement('canvas');
    const context = canvas.getContext('2d');

    // 设置更大的字体大小，使用加粗字体以确保文字清晰可见
    const fontSize = 48;
    context.font = `900 ${fontSize}px Arial`; // 使用900来表示最粗的字体

    // 限制名称长度
    const maxNameLength = 6;
    let displayName = name;
    if (name.length > maxNameLength) {
        displayName = name.substring(0, maxNameLength) + '...';
    }

    // 测量文字高度，调整行高以优化显示
    const charHeight = fontSize;
    // 减小行高值，使竖向字符间距更加紧凑
    const lineHeight = charHeight;  // 减小行高，使字符间距更紧凑

    // 将name分割为单个字符，实现竖向显示（每个字符占一行）
    const nameLines = displayName.split(''); // name竖向显示，每个字符占一行

    // 计算总高度，只基于name
    const totalLines = nameLines.length;

    // 计算最大字符宽度，确保足够的显示空间
    let maxCharWidth = 0;
    [...displayName].forEach(char => {
        const charWidth = context.measureText(char).width;
        maxCharWidth = Math.max(maxCharWidth, charWidth);
    });

    // 设置紧凑的canvas尺寸，适应较小的行高
    const canvasWidth = maxCharWidth + 15; // 适中的左右边距
    const canvasHeight = totalLines * lineHeight + 50; // 适中的上下边距
    canvas.width = canvasWidth;
    canvas.height = canvasHeight;

    // 设置文本对齐方式
    context.textAlign = 'center';
    context.textBaseline = 'middle';

    // 设置文字颜色
    context.fillStyle = `rgb(${((textColor >> 16) & 0xFF)}, ${((textColor >> 8) & 0xFF)}, ${(textColor & 0xFF)})`;

    // 绘制name（竖向显示，更紧凑排列）
    nameLines.forEach((char, index) => {
        // 使用紧凑的垂直位置计算，顶部留出少量边距  从顶部往下移动，避免文字被裁剪在顶
        const verticalPosition = 40 + index * lineHeight * 0.6;
        // 先绘制描边
        context.strokeText(char, canvasWidth / 2, verticalPosition);
        // 再绘制填充
        context.fillText(char, canvasWidth / 2, verticalPosition);
    });

    // 重置阴影设置，避免影响其他绘制
    context.shadowColor = 'transparent';
    context.shadowBlur = 0;
    context.shadowOffsetX = 0;
    context.shadowOffsetY = 0;

    // 创建纹理
    const texture = new THREE.CanvasTexture(canvas);
    texture.needsUpdate = true;

    // 创建文字材质
    const textMaterial = new THREE.MeshBasicMaterial({
        map: texture,
        transparent: true,
        side: THREE.DoubleSide
    });

    // 创建更大的侧面文字平面几何体，确保文字更加清晰可见
    const textGeometry = new THREE.PlaneGeometry(40, cubeHeight * 0.9);

    // 创建文字网格
    const textMesh = new THREE.Mesh(textGeometry, textMaterial);

    // 保存初始旋转，用于后续更新
    textMesh.initialRotationY = 0;

    // 设置文字位置和旋转，根据侧面不同
    const offset = FIXED_CUBE_SIZE / 2; // 调整为立方体大小的一半，使文字紧贴侧面显示

    switch (side) {
        case 'front': // Z轴正方向
            textMesh.position.set(0, 0, offset);
            textMesh.rotation.y = 0;
            textMesh.initialRotationY = 0;
            break;
        case 'back': // Z轴负方向
            textMesh.position.set(0, 0, -offset);
            textMesh.rotation.y = Math.PI;
            textMesh.initialRotationY = Math.PI;
            break;
        case 'left': // X轴负方向
            textMesh.position.set(-offset, 0, 0);
            textMesh.rotation.y = -Math.PI / 2;
            textMesh.initialRotationY = -Math.PI / 2;
            break;
        case 'right': // X轴正方向
            textMesh.position.set(offset, 0, 0);
            textMesh.rotation.y = Math.PI / 2;
            textMesh.initialRotationY = Math.PI / 2;
            break;
    }

    // 将文字添加到立方体
    cube.add(textMesh);
}

// 保持文字始终面向相机
function updateTextOrientation() {
    mountains.forEach(mountain => {
        // 调试：检查每个立方体的子对象数量
        console.log(`Mountain children count: ${mountain.children.length}`);

        // 尝试查找所有平面几何体子对象（可能是顶面文字）
        const planeGeometries = mountain.children.filter(child =>
            child.geometry instanceof THREE.PlaneGeometry
        );
        console.log(`Found ${planeGeometries.length} plane geometries`);

        // 恢复原始的顶面文字查找逻辑，基于旋转角度识别顶面文字
        const topTextMesh = mountain.children.find(child =>
            child.geometry instanceof THREE.PlaneGeometry &&
            Math.abs(child.rotation.x + Math.PI / 2) < 0.1
        );

        if (topTextMesh) {
            // 固定顶面文字，不旋转，始终贴在立方体顶部
            // 确保文字不随相机旋转而旋转
            topTextMesh.rotation.x = -Math.PI / 2; // 保持文字平面朝上（默认状态）
            topTextMesh.rotation.y = 0; // 固定Y轴旋转，不旋转
            topTextMesh.rotation.z = 0; // 固定Z轴旋转，不旋转

            // 确保文字材质是可见的
            if (topTextMesh.material) {
                topTextMesh.material.visible = true;
                topTextMesh.material.opacity = 1;
            }
        }

        // 查找侧面文字网格（不包括顶面文字）
        const sideTextMeshes = mountain.children.filter(child =>
            child.geometry instanceof THREE.PlaneGeometry &&
            Math.abs(child.rotation.x + Math.PI / 2) >= 0.1
        );

        sideTextMeshes.forEach(sideTextMesh => {
            // 复制相机的旋转
            const textRotation = sideTextMesh.rotation.clone();
            const cameraRotation = camera.rotation.clone();

            // 对于侧面文字，根据其初始旋转调整
            // 主要调整Y轴旋转，保持文字面向相机
            if (Math.abs(sideTextMesh.initialRotationY) < 0.1 ||
                Math.abs(sideTextMesh.initialRotationY - Math.PI) < 0.1) {
                // 前后侧面
                textRotation.y = cameraRotation.y + (sideTextMesh.initialRotationY || 0);
            } else {
                // 左右侧面
                textRotation.y = cameraRotation.y + (sideTextMesh.initialRotationY || 0);
            }

            // 固定X和Z轴旋转
            textRotation.x = 0;
            textRotation.z = 0;

            // 应用旋转
            sideTextMesh.rotation.copy(textRotation);
        });
    });
}

//加载JSON数据并生成立方体
function loadDataAndGenerateMountains() {
    mountains = generateMountainsFromJSON(stockData);

    // 动态创建或更新网格辅助，基于实际数据量
    if (gridHelper) {
        scene.remove(gridHelper);
    }

    // 使用全局配置的网格布局
    const totalWidth = GRID_COLUMNS * FIXED_CUBE_SIZE;
    const totalDepth = GRID_ROWS * FIXED_CUBE_SIZE;
    const maxDimension = Math.max(totalWidth, totalDepth);

    // 创建新的网格辅助，使用全局配置的网格参数，使用偏白色的网格线，确保清晰可见
    gridHelper = new THREE.GridHelper(maxDimension, GRID_COLUMNS, 0xCCCCCC, 0xAAAAAA); // 使用配置的网格线数量
    scene.add(gridHelper);
}

// 根据排序方式重新生成山峰
function regenerateMountainsBySort() {
    // 清除现有山峰
    mountains.forEach(mountain => {
        scene.remove(mountain);
    });
    mountains = [];

    // 重新生成山峰
    loadDataAndGenerateMountains();
}

// 创建时间控制UI
function createTimeControlUI() {
    const timeControl = document.createElement('div');
    timeControl.className = 'time-control';
    timeControl.innerHTML = `
        <button id="prev-button" title="上一个时间点">◀</button>
        <button id="play-button" title="播放/暂停">播放</button>
        <button id="next-button" title="下一个时间点">▶</button>
        <span id="time-display">时间点: 1 / ${TIME_SERIES_LENGTH}</span>
        <input type="range" id="time-progress" min="0" max="${TIME_SERIES_LENGTH - 1}" value="0" step="1" title="时间进度">
        <label for="speed-control">速度:</label>
        <select id="speed-control" title="动画速度">
            <option value="500">慢</option>
            <option value="200" selected>中</option>
            <option value="100">快</option>
            <option value="50">最快</option>
        </select>
        <label><input type="checkbox" id="show-trails" checked> 显示轨迹</label>
        <label for="sort-method">排序:</label>
        <select id="sort-method">
            <option value="weight">权重</option>
            <option value="increase">涨跌幅</option>
        </select>
    `;
    document.body.appendChild(timeControl);

    // 添加事件监听
    document.getElementById('prev-button').addEventListener('click', previousTimePoint);
    document.getElementById('play-button').addEventListener('click', togglePlayback);
    document.getElementById('next-button').addEventListener('click', nextTimePoint);
    document.getElementById('time-progress').addEventListener('input', function () {
        stopPlayback();
        isPlaying = false;
        document.getElementById('play-button').textContent = '播放';
        switchToTimePoint(parseInt(this.value));
    });
    document.getElementById('speed-control').addEventListener('change', function () {
        updateAnimationSpeed(parseInt(this.value));
    });
    document.getElementById('show-trails').addEventListener('change', toggleTrails);
    document.getElementById('sort-method').addEventListener('change', function () {
        currentSortMethod = this.value;
        regenerateMountainsBySort();
    });
}

// 创建鼠标交互控制器实例
let mouseController = null;

// 页面加载完成后执行
window.addEventListener('load', async () => {
    // 生成box
    loadDataAndGenerateMountains();

    // 初始化鼠标交互控制器，传入renderer以避免与UI控件的事件冲突
    mouseController = createMouseInteractionController(scene, camera, renderer,
        {
            rotateSpeed: 0.3,
            zoomSpeed: 0.5,
            minDistance: 200,
            maxDistance: 2000
        });

    // 创建时间控制UI
    createTimeControlUI();
});

// 渲染循环
function animate() {
    requestAnimationFrame(animate);

    // 更新文字方向，使其始终面向相机
    updateTextOrientation();

    // 更新鼠标交互控制器，确保阻尼效果正常工作
    if (mouseController) {
        mouseController.update();
    }

    renderer.render(scene, camera);
}

animate();


window.addEventListener('resize', () => {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
});
